package com.sadhoc.sockets;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.sadhoc.router.pdu.HDPacket;

public class TCPSender implements Runnable {
	
	private Socket socket 			= null;
	private ObjectOutputStream oos	= null;
	private Queue<HDPacket> queue 	= null;
	
	/*
	 * Constructor
	 */
	public TCPSender(String destIP, int port) throws UnknownHostException, IOException {
		
		socket = new Socket(InetAddress.getByName(destIP),port);
		oos = new ObjectOutputStream(socket.getOutputStream());
		queue = new ConcurrentLinkedQueue<HDPacket>();
		
	}
	
	/*
	 * Since TCP is connection oriented we need to send packets
	 * in a queue fashion, otherwise we may try to create a new
	 * tcp session that already exists. Therefore this method will
	 * while the session is active. If a message is added to the queue
	 * this method will write the HDPacket as a serialized object in
	 * the TCP flow
	 */
	public void run() {
		
		while (socket.isConnected()) {
			try {
				
				synchronized(queue) {
					while (queue.isEmpty()) {
						queue.wait();
					}
				}

				oos.writeObject(queue.poll());
				oos.flush();
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/*
	 * This method provides a thread safe mechanism for external classes
	 * to add message to the queue to be sent
	 */
	public void send(HDPacket hdp) {
		
		queue.add(hdp);
		synchronized (queue) {
			queue.notify();
		}
		
	}
	
	/*
	 * Thread safe closing of the TCP socket
	 */
	public void close() {
		
		synchronized (socket) {
			try {
				oos.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			socket.notify();
		}
		
	}
	
	/*
	 * Thread safe to check if the TCP session is still connected
	 */
	public synchronized boolean isConnected() {
		return socket.isConnected();
	}

}
